<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Grammar Nonterminals Test</title>
    <script src="./evidence.js"></script>
    <script src="/assets/application.js"></script>
  </head>
  <body>
    
    <p>Test results are logged to the console.</p>
    
    <script>
    (function(){
      
      function calculate(grammar, calculation) {
        
        return grammar.calculate(calculation);
        
      }
      
      function parse(spec) {
        
        return Grammar.parse(spec).grammar;
        
      }
      
      function isSetEqual(a, b) {
        
        return Set.count(Set.intersection(a, b)) === Set.count(a);
        
      }
      
      function isRelationEqual(a, b) {
        
        var k;
        
        if (Set.count(a) !== Set.count(b))
          return false;
        
        for (k in a) {
          if (!isSetEqual(a[k], b[k]))
            return false;
        }
        
        return true;
        
      }
      
      Evidence.Assertions.assertRelationEqual = function(expected, actual, message) {
        
        this._assertExpression(
          isRelationEqual(expected, actual),
          message || 'Failed assertion.',
          'Expected %o, got %o.', expected, actual
        )
        
      }
      
      Evidence.Assertions.assertSetEqual = function(expected, actual, message) {
        
        this._assertExpression(
          isSetEqual(expected, actual),
          message || 'Failed assertion.',
          'Expected %o, got %o.', expected, actual
        )
        
      }
      
      var Fixtures = {
        // Louden, p.170
        expressions: "exp -> exp addop term | term. \
addop -> + | -. \
term -> term mulop factor | factor. \
mulop -> *. \
factor -> ( exp ) | number.",
        
        // Louden, p.171
        ifelse: "statement -> if-stmt | other. \
if-stmt -> if ( exp ) statement else-part. \
else-part -> else statement | . \
exp -> 0 | 1.",

        // Louden, p.173
        statements: "stmt-sequence -> stmt stmt-seq' . \
stmt-seq' -> ; stmt-sequence | . \
stmt -> s."
      }

      Evidence("GrammarNonterminalsTest", {
        
        testFirst: function(t) {
          
          t.assertRelationEqual({
            "exp": { "(": true, "number": true },
            "term": { "(": true, "number": true },
            "factor": { "(": true, "number": true },
            "addop": { "+": true, "-": true },
            "mulop": { "*": true }
          }, calculate(parse(Fixtures.expressions), "grammar.first"));
          
          t.assertRelationEqual({
            "statement": { "if": true, "other": true },
            "if-stmt": { "if": true },
            "else-part": { "else": true },
            "exp": { "0": true, "1": true }
          }, calculate(parse(Fixtures.ifelse), "grammar.first"));
          
          t.assertRelationEqual({
            "stmt-sequence": { "s": true },
            "stmt-seq'": { ";": true },
            "stmt": { "s": true }
          }, calculate(parse(Fixtures.statements), "grammar.first"));
          
        },
        
        testFollow: function(t) {
          
          t.assertRelationEqual({
            "exp": { "Grammar.END": true, "+": true, "-": true },
            "term": { "Grammar.END": true, "+": true, "-": true, "*": true, ")": true },
            "factor": { "Grammar.END": true, "+": true, "-": true, "*": true, ")": true },
            "addop": { "(": true, "number": true },
            "mulop": { "(": true, "number": true }
          }, calculate(parse(Fixtures.expressions), "grammar.follow"));
          
          t.assertRelationEqual({
            "statement": { "Grammar.END": true, "else": true },
            "if-stmt": { "Grammar.END": true, "else": true },
            "else-part": { "Grammar.END": true, "else": true },
            "exp": { ")": true }
          }, calculate(parse(Fixtures.ifelse), "grammar.follow"));
          
          t.assertRelationEqual({
            "stmt-sequence": { "Grammar.END": true },
            "stmt-seq'": { "Grammar.END": true },
            "stmt": { ";": true, "Grammar.END": true }
          }, calculate(parse(Fixtures.statements), "grammar.follow"));
          
        },
        
        testNullable: function(t) {
          
          t.assertSetEqual({ }, calculate(parse(Fixtures.expressions), "grammar.nullable"));
          t.assertSetEqual({ "else-part": true }, calculate(parse(Fixtures.ifelse), "grammar.nullable"));
          t.assertSetEqual({ "stmt-seq'": true }, calculate(parse(Fixtures.statements), "grammar.nullable"));
          
        }
        
      });
      
    })();
    </script>
    
  </body>
</html>
